package cn.edu.thu.tsquality.core.common.table;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by wangyihan  on 2018/4/2 下午4:16. E-mail address is yihanwang22@163.com. Copyright © 2017
 * wangyihan. All Rights Reserved.
 *
 * @author wangyihan
 */
public class Header implements Serializable {

  private final Map<String, Integer> attrMap;
  private String[] attrList;
  private DataType[] schema;

  private boolean withDataType;

  public boolean isWithDataType() {
    return withDataType;
  }

  public DataType[] getSchema() {
    return schema;
  }

  public Header(Map<String, Integer> attrMap) {
    this.attrMap = attrMap;
    this.attrList = new String[attrMap.size()];
    this.schema = new DataType[attrMap.size()];
    withDataType = true;
    for (Map.Entry<String, Integer> entry : attrMap.entrySet()) {
      try {
        if (entry.getKey().trim().indexOf("@") < 0) {
          withDataType = false;
        }
        this.attrList[entry.getValue()] = entry.getKey();
        if (withDataType) {
          try {
            String[] tmpList = entry.getKey().trim().split("@");
            this.schema[entry.getValue()] = DataType.getDataType(tmpList[1]);
          } catch (IndexOutOfBoundsException e1) {
            withDataType = false;
            this.schema = null;
          } catch (IllegalArgumentException e2) {
            withDataType = false;
            this.schema = null;
          }
        }
      } catch (IndexOutOfBoundsException e) {
        throw new IndexOutOfBoundsException(
            "Index Out bound when initial header info: " + e.getMessage());
      }
    }
    if (!withDataType) {
      this.schema = null;
    }
  }

  public Header(String[] attrs) {
    this.attrList = attrs;
    this.schema = new DataType[attrs.length];
    Map<String, Integer> tempMap = new HashMap<>();
    withDataType = true;
    for (int i = 0; i < attrs.length; ++i) {
      tempMap.put(attrs[i], i);
      if (attrs[i].trim().indexOf("@") < 0) {
        withDataType = false;
      }
      if (withDataType) {
        try {
          String[] tmpList = attrs[i].trim().split("@");
          this.schema[i] = DataType.getDataType(tmpList[1]);
        } catch (IndexOutOfBoundsException e1) {
          withDataType = false;
          this.schema = null;
        } catch (IllegalArgumentException e2) {
          withDataType = false;
          this.schema = null;
        }
      }
    }
    this.attrMap = tempMap;
    if (!withDataType) {
      this.schema = null;
    }
  }

  public int getIndex(String attr) {
    if (!attrMap.containsKey(attr)) {
      return -1;
    }
    return attrMap.get(attr);
  }

  public String getAttrName(int index) {
    if (index >= attrList.length || index < 0) {
      throw new IllegalArgumentException(
          String.format("Getting attrName for index %d but header only has %d " +
              "values!", new Object[]{index, this.attrList.length}));
    }
    return attrList[index];
  }

  public Map<String, Integer> getMapping() {
    return this.attrMap;
  }

  public String[] toArray() {
    return this.attrList;
  }

  public int size() {
    return this.attrList.length;
  }

  public boolean remove(String attr) {
    if (!attrMap.containsKey(attr)) {
      return false;
    }
    String[] temp = new String[attrList.length - 1];
    DataType[] tempdt = null;
    if (withDataType) {
      tempdt = new DataType[schema.length - 1];
    }
    boolean found = false;
    for (int i = 0; i < attrList.length; ++i) {
      String tempAttr = attrList[i];
      if (!found && tempAttr.equals(attr)) {
        attrMap.remove(attr);
        found = true;
      } else if (!found) {
        temp[i] = attrList[i];
        if (withDataType) {
          tempdt[i] = schema[i];
        }
      } else {
        temp[i - 1] = tempAttr;
        if (withDataType) {
          tempdt[i - 1] = schema[i - 1];
        }
        attrMap.put(tempAttr, attrMap.get(tempAttr) - 1);
      }
    }
    this.attrList = temp;
    this.schema = tempdt;
    return true;
  }

  public DataType getDataType(Integer idx) {
    if (!withDataType) {
      throw new NullPointerException(String.format("Header without schema!"));
    }
    if (idx >= schema.length || idx < 0) {
      throw new IllegalArgumentException(
          String.format("Getting schema for index %d but header only has %d " +
              "values!", new Object[]{idx, this.schema.length}));
    }
    return schema[idx];
  }

  public DataType getDataType(String attrName) {
    if (!withDataType) {
      throw new NullPointerException(String.format("Header without schema!"));
    }
    return getDataType(getIndex(attrName));
  }

  public String getDataTypeString(String attrName) {
    if (!withDataType) {
      throw new NullPointerException(String.format("Header without schema!"));
    }
    int idx = getIndex(attrName);
    DataType dt = getDataType(idx);
    return DataType.getDataTypeString(dt);
  }
}